home *** CD-ROM | disk | FTP | other *** search
Korn shell script | 1997-08-26 | 12.1 KB | 389 lines |
- #!/bin/ksh
- # @(#) te.ksh,where.ksh 2.1.1 97/07/20
- # 92/08/31 John H. DuBois III (john@armory.com)
- # 92/09/10 added -p option to whence
- # 92/10/16 Allow multiple ,-separated targets; added help
- # 92/10/21 Added {}
- # 93/08/02 Changed name to te to avoid conflict with to
- # 94/03/08 Added -c option
- # 94/06/29 Added -xpt. Fixed bug in {} processing which caused all target
- # executables to be substituted for {} as a single word.
- # 95/04/23 Added TECMD env var, n option, & rcfile processing
- # 95/05/17 Change window title if appropriate
- # 96/08/24 Let capital letters turn off options. Merged te and where.
- # Added aL options.
- # 96/09/03 Do not search for absolute paths in $PATH.
- # 97/07/20 2.1.1 In 'where' mode, print each match as it is found rather than
- # storing in an array, to avoid 1024-element limit.
-
- # todo: option to cd to directory that 1st file is found in before running
- # command.
-
- name=${0##*/}
- iUsage="$name [-aAeEfFhlLnsStTvVwW] [-p<path>]"
- Usage=\
- "Usage:
- $iUsage target[,target,...] [cmd [arg ...]]
- $iUsage -c \"cmd [arg ...]\" target [target ...]"
-
- # Only take TECMD from the environment
- Debug=false Test=false VERBOSE= notVERBOSE=false Readrc=true CmdGiven=false
- ALL= notALL=false NOTITLE= notNOTITLE=false ANYFILE= notANYFILE=false
- SYMLINKS= notSYMLINKS=false EXACT= notEXACT=false SHELLPAT= notSHELLPAT=false
- listTargets=false where=false types=
- rcFile=.terc
- searchPath=$PATH
-
- if [ "$name" = where ]; then
- listTargets=true SHELLPAT=1 Readrc=false where=true
- fi
-
- while getopts :aAc:eEfFhlLnp:sStTvVwWx opt; do
- case $opt in
- h)
- echo \
- "$name: execute commands on commands.
- $Usage
- Each target is searched for as a command in \$PATH and expanded into its
- full filename. command [arg ...] is then executed with the list of
- filenames as additional arguments. Targets given with absolute paths are
- operated on directly without searching for them in \$PATH (as is the behaviour
- when a program name is given to the shell to be executed).
- If one of the arguments is \"{}\", it is replaced with the list of
- filenames and the filenames are not passed as additional arguments.
- Targets can be specified either as a comma-separated list followed by the
- command and its optional initial arguments, or if the command is given with -c,
- as a list of separate arguments following the command (which in this case must
- be quoted to make it a single argument if it includes initial arguments to the
- command).
- If the environment variable TECMD is set, it provides a default command and
- optional initial arguments to it to run if no command is specified (if only
- one argument is given and -c is not given). TECMD may also be assigned a value
- in the configuration file described below.
- If the environment variables WINTITLE, DISPLAY, and EDITOR are set, and the
- command to be run is the same as the value of EDITOR, an xterm escape sequence
- is sent to change the window title to the name of the files being edited,
- followed by the value of WINTITLE in parentheses. After editing is completed,
- another sequence is sent to change the window title back to the value of
- WINTITLE by itself.
-
- Options:
- Some of the following options can be set in a configuration file named
- \"$rcFile\", which may be in the invoking user's home directory or in the
- directory specified by the environment variable UHOME (if both exist,
- assignments in the former take precedence). In this file, values are assigned
- to variables in shell style, e.g. \"VARNAME=value\". To set an option that
- does not take a value, use \"VARNAME=1\". Options given in the configuration
- files are overridden by those given on the command line. Use the upper case
- version of a Boolean (on or off) option letter to unset it. Variable names are
- given in parentheses following option descriptions.
- Example contents of a $rcFile file:
- TECMD=\"vi -c'set ts=4'\"
- If this program is invoked as \"where\", the T, s, and n options are turned on.
- -a: Execute the command on every instance of the target that occurs in the
- search path, instead of just the first occurance. (ALL)
- -s: Treat filenames as unanchored ksh shell patterns. This causes e.g. the
- pattern \"bar@(ba|fi)z\" to select filenames that contain the string barbaz
- or barfiz. See the regexp(M) man page for a description of ksh filename
- patterns. Patterns are surrounded by implicit '*' characters, causing them
- to be unanchored. This can be overridden by using '^' and '$' to force a
- match to start at the beginning and end at the end of a filename
- respectively. Characters that are special to the shell must generally
- be protected from the shell by surrounding them with quotes. (SHELLPAT)
- -e: Used with -s, finds exact (complete) matches only; equivalent to putting ^
- and $ at the start and end of each pattern. (EXACT)
- -f: Find any matching regular file in the search path, regardless of whether it
- is executable or not. (ANYFILE)
- -l: Also find any matching symlink in the search path, regardless of whether
- the file it points to exists or is executable. (SYMLINKS)
- -p<path>: Use <path> as the search path instead of using the value of the PATH
- variable.
- -h: Print this help.
- -n: Do not read the $rcFile file.
- -w: Do not send an xterm title escape sequence. (NOTITLE)
- -v: Print the command before it is run. (VERBOSE)
- -t: Print the command that would be executed, but do not run it.
- -T: Print a list of all matching target files, without doing anything to them.
- This turns on the a option."
- exit 0
- ;;
- c)
- Cmd=$OPTARG
- CmdGiven=true
- ;;
- x) Debug=true;;
- w) NOTITLE=1;;
- W) notNOTITLE=true;;
- n) Readrc=false;;
- t) Test=true;;
- T) listTargets=true;;
- v) VERBOSE=1;;
- V) notVERBOSE=true;;
- p) searchPath=$OPTARG;;
- f) ANYFILE=1;;
- F) notANYFILE=true;;
- e) EXACT=1;;
- E) notEXACT=true;;
- s) SHELLPAT=1;;
- S) notSHELLPAT=true;;
- l) SYMLINKS=1;;
- L) notSYMLINKS=true;;
- a) ALL=1;;
- A)
- notALL=true;;
- +?)
- print -ru2 -- "$name: options should not be preceded by a '+'."
- exit 1
- ;;
- :)
- print -r -u2 -- \
- "$name: Option '$OPTARG' requires a value. Use -h for help."
- exit 1
- ;;
- :)
- print -r -u2 -- \
- "$name: Option '$OPTARG' requires a value. Use -h for help."
- exit 1
- ;;
- ?)
- print -ru2 -- "$name: $OPTARG: bad option. Use -h for help."
- exit 1
- ;;
- esac
- done
-
- # remove args that were options
- let OPTIND=OPTIND-1
- shift $OPTIND
-
- function CheckPat {
- typeset file pattern=$1 t
- typeset -i ret=0
-
- [ -n "$SHELLPAT" ] && set -- $pattern || set -- "$pattern"
- for file; do
- if [ -x "$file" -a -f "$file" ]; then
- if $listTargets; then
- print "$file"
- else
- Files[type_i]=$file
- [ -z "$ALL" ] && return
- fi
- ret=1
- let type_i+=1
- else
- for t in $types; do
- if [ -$t "$file" ]; then
- if $listTargets; then
- print "$file"
- else
- Files[type_i]=$file
- [ -z "$ALL" ] && return
- fi
- ret=1
- let type_i+=1
- fi
- done
- fi
- done
- return $ret
- }
-
- # Usage: PathSearch pattern
- # Sets Files[] to all files in Paths[] that match the pattern
- # type_i is set to the number of matches, which is the number of elements
- # put in Files[] if listTargets is false.
- typeset -i type_i
- function PathSearch {
- typeset arg=$1 found=false PathElem
-
- set +f # make sure filename globbing is on
- unset Files
- type_i=0
- if [ -n "$SHELLPAT" -a -z "$EXACT" ]; then
- # Discard anchor chars if given, since they are not real metachars
- if [[ "$arg" = ^* ]]; then
- arg=${arg#?}
- else
- arg="*$arg" # Pattern is not anchored at start
- fi
- if [[ "$arg" = *\$ ]]; then
- arg=${arg%?}
- else
- arg="$arg*" # Pattern is not anchored at end
- fi
- fi
- if [[ "$arg" = /* ]]; then
- CheckPat "$arg"
- else
- # Find all pattern matches that are executable regular files.
- for PathElem in "${Paths[@]}"; do
- CheckPat "$PathElem/$arg"
- done
- fi
- }
-
- # This code must come after arg processing because until args are processed
- # we do not know if we should read the rcfile or not.
- rcFile=$HOME/$rcFile
- # Let TECMD in the environment override rcfile
- oTECMD=$TECMD
- $Readrc && [ -f "$rcFile" ] && . $rcFile
- [ -n "$oTECMD" ] && TECMD=$oTECMD
- [ -z "$Cmd" -a -n "$TECMD" ] && Cmd=$TECMD
-
- $listTargets && ALL=1
- $notNOTITLE && NOTITLE=
- $notVERBOSE && VERBOSE=
- $notALL && ALL=
- $notANYFILE && ANYFILE=
- $notSYMLINKS && SYMLINKS=
- $notSHELLPAT && SHELLPAT=
- $notEXACT && EXACT=
- [ -z "$SHELLPAT" ] && EXACT=
- [ -n "$SYMLINKS" ] && types=L
- [ -n "$ANYFILE" ] && types="$types f"
-
- # $Cmd is now either a command given with -c or a default command (if any).
- # If a command was given with -c, each remaining arg is a target.
- # Otherwise, $1 is a comma-separated list of targets; any further arguments
- # are the command and its args.
-
- typeset -i MinArgs
- # If we have a default or -c command, need only one arg (target).
- # Otherwise, need at least two (list of targets and command).
- [ "$name" = where -o -n "$Cmd" ] && MinArgs=1 || MinArgs=2
- if [ $# -lt MinArgs ]; then
- print -ru2 "$Usage
- Use -h for help."
- exit
- fi
-
- if $CmdGiven; then
- # -c given; all args are targets
- # Save targets, since the next set -A will wipe out positional params
- set -A targets -- "$@"
- # eval this so that quoted options can be given in Cmd
- eval set -A command $Cmd
- else
- # First arg is a comma-separated list of targets.
- targets=$1
- shift 1
- if [ $# -eq 0 ]; then
- # eval this so that quoted options can be given in Cmd
- eval set -A command $Cmd
- else
- set -A command "$@"
- fi
- OFS=$IFS
- IFS=,
- set -A targets -- $targets
- IFS=$OFS
- fi
-
- targList="$*"
-
- $Debug && print -ru2 "Command: <${command[*]}> Targets: <${targets[*]}>"
-
- # Command & args now reside in command[];
- # target executables are in targets[]
-
- # If we need to get every instance of a match in the search path (ALL),
- # or need to get both executable and non-executable matches (ANYFILE),
- # or need to get dangling symlinks (SYMLINKS),
- # or need to be able to match shell patterns (SHELLPAT),
- # then we must do our own search. Otherwise we can just use 'whence'.
- if [ -n "$ALL" -o -n "$ANYFILE" -o -n "$SYMLINKS" -o -n "$SHELLPAT" ]; then
- OIFS=$IFS
- IFS=: # Make PATH be split on :
- set -A Paths -- $PATH
- IFS=$OIFS
- LongSearch=true
- else
- LongSearch=false
- fi
-
- # Get full paths to matches.
- oPATH=$PATH
- PATH=$searchPath
- typeset -i numMatch=0
- for target in "${targets[@]}"; do
- if $LongSearch; then
- PathSearch "$target"
- else
- set -A Files -- "$(whence -p $target)" # quick search
- [ -z "$Files" ] && unset Files
- type_i=${#Files[*]}
- fi
- if [ type_i -eq 0 ]; then
- print -ru2 -- "$target: not found"
- else
- $listTargets ||
- set -A TargetExecutables -- "${TargetExecutables[@]}" "${Files[@]}"
- fi
- let numMatch+=type_i
- done
-
- PATH=$oPATH
- # Quit if there were no good targets
- [ numMatch -eq 0 ] && exit 1
-
- $Debug && print -ru2 "Number of matches: $numMatch"
-
- $listTargets && exit 0
-
- #if $listTargets; then
- # IFS="
- #"
- # print -r -- "${TargetExecutables[*]}"
- # exit 0
- #fi
-
- # Replace any {} with the list of target executables
- typeset -i CmdInd=0 DestInd=0
- NoAppend=false
- while [ CmdInd -lt ${#command[*]} ]; do
- if [ "${command[CmdInd]}" = {} ]; then
- # Stick target args at end of cmd line being built
- set -A FullCmd -- "${FullCmd[@]}" "${TargetExecutables[@]}"
- DestInd=${#FullCmd[*]} # Recalculate dest index
- NoAppend=true
- else
- FullCmd[DestInd]=${command[CmdInd]}
- let DestInd+=1
- fi
- let CmdInd+=1
- done
-
- $NoAppend && unset TargetExecutables[*]
-
- if $Test; then
- print -ru2 "Would execute: ${FullCmd[@]} ${TargetExecutables[@]}"
- exit 0
- fi
-
- a0=${FullCmd[0]}
- [[ -z "$WINTITLE" || -z "$DISPLAY" || -n "$NOTITLE" || \
- ${a0##*/} != "${EDITOR##*/}" ]] && NOTITLE=true || NOTITLE=false
-
- [ -n "$VERBOSE" ] &&
- print -ru2 "Executing: ${FullCmd[@]} ${TargetExecutables[@]}"
- $Debug && { print -ru2 "Press return to continue..."; read; }
-
- function ResetTitle {
- print -n "\033]0;$OWINTITLE\007"
- }
-
- if $NOTITLE; then
- exec "${FullCmd[@]}" "${TargetExecutables[@]}"
- else
- OWINTITLE=$WINTITLE
- # Change WINTITLE in environment, so that in case another app that
- # uses WINTITLE is run from the editor, it will use modified value
- WINTITLE="$targList ($OWINTITLE)"
- trap ResetTitle INT QUIT
- print -n "\033]0;$WINTITLE\007" # set win title
- "${FullCmd[@]}" "${TargetExecutables[@]}"
- ResetTitle
- fi
-